触发器和事件

数据库自己"自动干活"的两种方式

🎯 本章你需要掌握的(按重要程度排序)
  • 触发器是什么、什么时候自动触发 必考
  • BEFORE / AFTER 触发器的区别 必考
  • NEW 和 OLD 怎么用 必考核心
  • 会写简单的 CREATE TRIGGER 语句 必考
  • 事件是什么:定时执行的"临时触发器" 必考
  • AT(一次)vs EVERY(周期) 必考
  • 触发器 vs 事件 的区别 必考

💡 先想象两个场景:

① 学生选了一门新课,但成绩还没出。系统应该 自动把成绩设为 0(而不是 NULL)。

② 项目临时表里堆积了大量数据。希望 每周自动清空一次

👉 这两件事都不需要"手动操作" —— 数据库可以自己干。

  • 触发器(TRIGGER):当某种 数据操作 发生时自动跑(INSERT/UPDATE/DELETE)
  • 事件(EVENT):当某个 时间点 到了自动跑("每周清空一次")

本章就讲这两个让数据库 "自动干活" 的工具。

一、触发器是什么?必考

1.1 一句话定义

📖 定义

触发器(Trigger) = 一种 "自动触发" 的存储过程。当对表执行 INSERT / UPDATE / DELETE 时,系统自动调用,无需手动 CALL。

🌰 通俗类比

触发器就像 "门口的红外感应灯"

① 平时不亮(不工作)

② 一旦有人进门(INSERT/UPDATE/DELETE 发生),自动亮起来(执行预设逻辑)

跟存储过程的区别在于:"存储过程要 手动 CALL,触发器是 自动跑"。

1.2 BEFORE 还是 AFTER?必考

触发器的执行时机有两种:

🛡️BEFORE 触发器

INSERT/UPDATE/DELETE 之前 执行。

👉 常用于:数据校验、修改新值(在数据真正写入前先处理)。

例:插入成绩为空时,先改成 0 再插入

📝AFTER 触发器

INSERT/UPDATE/DELETE 之后 执行。

👉 常用于:记日志、做后续操作(数据已经写好了,再做关联处理)。

例:修改专业后,记录到修改日志表

⭐ 选择口诀

"先校验用 BEFORE,后记录用 AFTER"

1.3 触发器能干什么?了解

📋 6 大应用场景
  • 数据校验(如:成绩不能小于 0)
  • 自动赋值(如:插入时空值改成默认值)
  • 记录日志(如:谁改了什么,什么时候改的)
  • 级联操作(如:删学生时同时删该学生的选课)
  • 复杂完整性约束(普通 CHECK 解决不了的)
  • 统计计算(如:插入订单时自动累加金额)

二、NEW 和 OLD(核心难点)必考核心

2.1 为什么需要 NEW 和 OLD?

触发器要操作"正在被改的那一行"。问题是:

  • 怎么拿到 "新值"?(INSERT 进来的、UPDATE 后的)
  • 怎么拿到 "旧值"?(UPDATE 前的、被 DELETE 的)

👉 答案:用 NEWOLD 这两个 "伪记录"

📖 定义

NEW = 这次操作的 "新数据"(正要插入的、修改后的)

OLD = 这次操作的 "旧数据"(修改前的、要被删除的)

用法:NEW.字段名OLD.字段名,比如 NEW.scoreOLD.maj

2.2 三种操作中 NEW 和 OLD 的对应关系必考

操作NEW(新数据)OLD(旧数据)说明
INSERT ✅ 有 ❌ 没有 插入时只有新值,没有旧值
UPDATE ✅ 有 ✅ 有 修改时新旧值都有
DELETE ❌ 没有 ✅ 有 删除时只有旧值,没有新值
⭐ 必背口诀

"插入用 NEW,删除用 OLD,修改两个都能用"

⚠️ 易错点

① 在 INSERT 触发器里写 OLD.字段报错,没有旧数据

② 在 DELETE 触发器里写 NEW.字段报错,没有新数据

三、创建触发器:完整体验实操必考

3.1 语法模板必考

📋 CREATE TRIGGER 模板
CREATE TRIGGER 触发器名
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON 表名
FOR EACH ROW
BEGIN
    -- 触发时执行的 SQL
END

关键点:

  • BEFORE / AFTER:触发时机(前/后)
  • INSERT / UPDATE / DELETE:触发事件(什么操作触发)
  • ON 表名:监视哪张表
  • FOR EACH ROW:受影响的每一行都触发一次(必写)
⚠️ 别忘了 DELIMITER!

触发器内部有多条以 ; 结尾的语句,跟存储过程一样要先 DELIMITER 改定界符。这是必考易错点。

3.2 例 1:BEFORE INSERT —— 自动校验必考

需求

插入成绩到 sc 表时,如果成绩为空,自动改成 0

SQL · BEFORE INSERT 触发器
DELIMITER $$

CREATE TRIGGER insert_sc_trigger
BEFORE INSERT ON sc
FOR EACH ROW
BEGIN
    IF NEW.score IS NULL THEN
        SET NEW.score = 0;
    END IF;
END $$

DELIMITER ;

-- 验证:插入一条 score 为 NULL 的记录
INSERT INTO sc VALUES ('s2', 'c8', NULL);

-- 查询结果:score 不是 NULL,而是 0
SELECT * FROM sc WHERE sno='s2' AND cno='c8';
💬 解读

BEFORE INSERT ON sc:在向 sc 表插入数据 之前 触发

NEW.score:拿到本次插入的新成绩

关键:在 BEFORE 触发器里 SET NEW.score = 0真的会改写写入数据库的值

④ 所以 INSERT 的 NULL 最后变成了 0 存进去

3.3 例 2:AFTER UPDATE —— 自动记日志必考

需求

修改学生专业(maj 字段)时,自动把修改前后的值记录到日志表

先建一张日志表:

SQL · 准备日志表
CREATE TABLE update_s_log (
    sno     CHAR(5)     NOT NULL,
    sn      VARCHAR(10)  NOT NULL,
    omaj    VARCHAR(100) NOT NULL,    -- 旧专业
    nmaj    VARCHAR(100) NOT NULL,    -- 新专业
    udate   DATETIME      NOT NULL     -- 修改时间
);

然后创建触发器:

SQL · AFTER UPDATE 触发器
DELIMITER $$

CREATE TRIGGER update_s_trigger
AFTER UPDATE ON s
FOR EACH ROW
BEGIN
    INSERT INTO update_s_log VALUES
    (OLD.sno, OLD.sn, OLD.maj, NEW.maj, NOW());
END $$

DELIMITER ;

-- 验证:修改 s2 的专业
UPDATE s SET maj = '计算机' WHERE sno = 's2';

-- 查询日志:会看到一条修改记录
SELECT * FROM update_s_log;
💬 解读

AFTER UPDATE:修改 之后 才记日志(数据已经改了,记录"原来是什么、现在是什么")

OLD.maj:修改前的专业

NEW.maj:修改后的专业

这是 UPDATE 触发器的典型用法 —— OLD 和 NEW 都用上

3.4 例 3:BEFORE DELETE —— 级联删除必考

需求

删除学生表 s 的某条记录时,自动把该学生在 sc 表中的所有选课记录也删掉

SQL · BEFORE DELETE 触发器
DELIMITER $$

CREATE TRIGGER delete_s_trigger
BEFORE DELETE ON s
FOR EACH ROW
BEGIN
    DELETE FROM sc WHERE sno = OLD.sno;
END $$

DELIMITER ;

-- 验证:删除学号 s1 的学生
DELETE FROM s WHERE sno = 's1';

-- s1 在 sc 表中的所有选课记录也会被自动删除
SELECT * FROM sc WHERE sno = 's1';   -- 空
💬 解读

BEFORE DELETE:在删除 s 表的记录 之前,先去删 sc 表里相关的

OLD.sno:被删学生的学号(DELETE 只有 OLD 没有 NEW)

③ 这就是"级联删除" —— 一个操作引发另一个

3.5 删除触发器

SQL
DROP TRIGGER IF EXISTS insert_sc_trigger;
⚠️ 注意

① 触发器 不能用 ALTER 修改 —— 只能"先删除再重新创建"

② 删除一个表时,该表上的所有触发器 会被自动删除

四、事件:定时执行必考

4.1 事件是什么?

📖 定义

事件(Event) = 在指定时间点(或周期)自动执行的"任务"。也叫"临时性触发器"。

🌰 跟触发器对比

触发器 像"红外感应灯" —— 有人来了才亮(数据被改才触发)

事件 像"定时浇花器" —— 到点就工作(不管有没有事都执行)

4.2 用事件之前:开启调度器必考

事件由"事件调度器"(Event Scheduler)来管理。必须先开启它,事件才会跑。

SQL · 调度器操作
-- 查看调度器状态
SHOW VARIABLES LIKE 'event_scheduler';

-- 开启调度器(如果是 OFF 的话)
SET GLOBAL event_scheduler = ON;

-- 关闭调度器
SET GLOBAL event_scheduler = OFF;
⚠️ 重要

调度器没开,事件不会自动执行!这是初学者最容易踩的坑。

4.3 创建事件:语法必考

📋 CREATE EVENT 模板
CREATE EVENT 事件名
ON SCHEDULE 时间调度规则
DO
    -- 要执行的 SQL 语句

时间调度规则有两种

⏰ AT(一次性)

在某个时刻执行一次,执行完就完了。

AT '2024-12-31 23:59:59'

AT NOW() + INTERVAL 10 SECOND

🔁 EVERY(周期性)

每隔一段时间执行一次,永远循环。

EVERY 1 DAY

EVERY 5 SECOND

⭐ 速记

AT = "在某时" → 执行一次

EVERY = "每隔" → 周期执行

4.4 例 1:AT —— 一次性事件必考

需求

10 秒后,往 tb_one 表插入一条记录。

SQL · 一次性事件
CREATE EVENT event_once
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 10 SECOND
DO
INSERT INTO tb_one VALUES (0, NOW());
💬 解读

AT 时刻:在指定的时刻执行一次

CURRENT_TIMESTAMP + INTERVAL 10 SECOND:当前时间 + 10 秒(即 10 秒后)

DO ...:要执行的 SQL(不止一条时用 BEGIN…END 包起来)

④ 10 秒后,事件自动执行,然后消失

4.5 例 2:EVERY —— 周期性事件必考

需求

每周清空一次 tb_one

SQL · 周期性事件
CREATE EVENT event_weekly_clean
ON SCHEDULE EVERY 1 WEEK
DO
DELETE FROM tb_one;

多语句的事件需要 BEGIN…END

SQL · 复杂事件
DELIMITER $$

CREATE EVENT event_weekly_score
ON SCHEDULE EVERY 1 WEEK
DO
BEGIN
    INSERT INTO week_score
    SELECT sno, AVG(score), SUM(score), NOW()
    FROM sc GROUP BY sno;
END $$

DELIMITER ;

这个事件每周自动跑一次,把每个学生的平均分、总分汇总到 week_score 表里。

4.6 删除事件

SQL
DROP EVENT IF EXISTS event_weekly_clean;
💡 修改事件用 ALTER EVENT

跟触发器不同,事件可以用 ALTER EVENT 修改(如改频率、改名字等),但本章浅讲,了解一下即可

五、触发器 vs 事件 对比必考

5.1 核心区别必考

🚪 触发器(Trigger)
  • 表的 INSERT/UPDATE/DELETE 触发
  • BEFORE / AFTER 之分
  • 可以使用 NEW / OLD
  • "有人来才亮"
⏰ 事件(Event)
  • 时间 触发(AT 或 EVERY)
  • 需要 事件调度器 开启
  • 没有 NEW / OLD(不针对特定行)
  • "到点就工作"

对比表

对比项触发器 (TRIGGER)事件 (EVENT)
触发条件 表的 INSERT/UPDATE/DELETE 到达某个时间点
关键字 BEFORE / AFTER AT / EVERY
NEW / OLD ✅ 可以使用 ❌ 不能使用
修改方式 不能 ALTER,必须删除重建 可以 ALTER EVENT 修改
典型用途 数据校验 / 记日志 / 级联 定时清理 / 定时统计 / 定时备份
⭐ 一句话区分

"触发器响应数据变化,事件响应时间到来"

六、本章小结

📋 三句话总结

🎯 核心内容
  • 触发器 = 表的"自动响应",由 INSERT/UPDATE/DELETE 触发,关键是 BEFORE/AFTER + NEW/OLD
  • 事件 = 数据库的"定时器",由时间触发,关键是 AT(一次)/EVERY(周期)必须先开调度器
  • 两者都能让数据库 "自己干活",区别在于触发条件不同。

本章必考点回顾

⭐ 期末考点

  1. NEW 和 OLD 在 INSERT/UPDATE/DELETE 中的对应关系
  2. BEFORE vs AFTER 的选择(前校验后记录)
  3. 写一个简单触发器(实操题)
  4. AT vs EVERY 的区别
  5. 触发器 vs 事件 的对比(简答题)

课堂综合测验

第 1 题

在一个 INSERT 触发器中,下列写法 错误 的是?

A. SET NEW.score = 0;
B. SET OLD.score = 0;
C. IF NEW.score IS NULL THEN ...
D. INSERT INTO log VALUES (NEW.id, NOW());

✅ 错的是:B

INSERT 触发器中没有 OLD(因为是新插入的,没有"旧值"可言)。

口诀:"插入用 NEW,删除用 OLD,修改两个都能用"

第 2 题

下列场景应该用 BEFORE 触发器 还是 AFTER 触发器

"插入员工记录时,如果工资为空就改为 3000 再存入"

A. BEFORE INSERT 触发器
B. AFTER INSERT 触发器
C. BEFORE UPDATE 触发器
D. AFTER DELETE 触发器

✅ 答案:A · BEFORE INSERT

分析:

① 要 修改新值(把 NULL 改成 3000)→ 用 BEFORE

插入事件 → INSERT

所以是 BEFORE INSERT

口诀:"先校验用 BEFORE,后记录用 AFTER"

第 3 题

关于事件(Event),下列说法 错误 的是?

A. AT 用于一次性执行,EVERY 用于周期性执行
B. 事件需要事件调度器开启才能正常工作
C. 事件中可以使用 NEW 和 OLD 来引用数据
D. 事件的多条 SQL 需要用 BEGIN...END 包起来

✅ 错的是:C

事件 不能用 NEW/OLD。NEW/OLD 是触发器特有的,因为触发器关联到具体的"行"操作。

事件是"到时间就跑",并不关联任何行,所以没有 NEW/OLD 概念。

第 4 题

下列需求中,最适合用事件 而不是触发器实现的是?

A. 插入订单时,自动校验金额不为负
B. 修改用户密码时,记录修改日志
C. 每天凌晨 2 点自动备份订单表
D. 删除部门记录时,同步删除该部门的所有员工

✅ 答案:C

分析:

A、B、D 都是"由数据操作触发"(INSERT/UPDATE/DELETE 时自动跑)→ 用 触发器

C 是"到指定时间触发"(每天凌晨 2 点)→ 用 事件

口诀:"触发器响应数据变化,事件响应时间到来"